package org.koyad.portal.parser;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;

import org.koyad.portal.annotations.TagCordinates;
import org.koyad.portal.annotations.TagType;
import org.koyad.portal.constants.FrameworkConstants;
import org.koyad.portal.context.PistonContext;
import org.koyad.portal.exceptions.ErrorCodes;
import org.koyad.portal.exceptions.FrameworkException;
import org.koyad.portal.tags.AbstractTag;
import org.koyad.portal.tags.Attribute;
import org.koyad.portal.tags.piston.TileTag;
import org.koyad.portal.utils.LogUtils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Used to create portal page view
 * @author Toasty
 *
 */
public class TagsHandler extends DefaultHandler {
	
	private static final LogUtils LOGGER = LogUtils.getLogger(TagsHandler.class);
	
	protected String _tmpText = "";
	private AbstractTag _tmpTag = null;
	
	public AbstractTag getTree() {
		return _tmpTag;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void startElement(String uri, String localName, String qName, Attributes xmlAtts) throws SAXException {
			
			List<Attribute> atts = null;
			//this will be the case while parsing portal page
			if(null == uri || uri.equals("")) {
				atts = getAttributesFromPage(xmlAtts);
			} else {
				atts = getAttributesFromXML(xmlAtts);
			}
			
			String tagClass = null;
			if(checkTile(atts)) {
				//this is a div tag representing tile tag
				tagClass = TileTag.class.getName();
			} else {
				String namespace = uri;
				if(namespace.equals("")) {
					namespace = FrameworkConstants.HTML5_NAMESPACE;
				}
				//other tags
				PistonContext pistonCtx = PistonContext.getContext();
				Map<QName, String> tags = (Map<QName, String>) pistonCtx.getApplication().getAttribute(
						FrameworkConstants.PISTON_TAGS);
				tagClass = tags.get(new QName(namespace, localName));
			}
		
			// create tag object
			AbstractTag tag = null;
			try {
				tag = (AbstractTag) Class.forName(tagClass).newInstance();
				tag.setAttributes(atts);
				
				if(!_tmpText.equals("")) {
					tag.setPreStartTagText(_tmpText);
					_tmpText = "";
				}
				
				if(null != _tmpTag) {
					tag.setParent(_tmpTag);
					_tmpTag.addChild(tag);
				}
				_tmpTag = tag;
				
				tag.postInit();
				
			} catch (Exception e) {
				LOGGER.logException(e);
				throw new FrameworkException(ErrorCodes.FWKTH001);
			}
	}

	@Override
	public void characters(char[] chars, int start, int end) throws SAXException {
		String text = new String(chars, start, end);
		TagCordinates anno = _tmpTag.getClass().getAnnotation(TagCordinates.class);
		try {
			if(anno.type().equals(TagType.SIMPLE)) {
				_tmpTag.setBodyText(text);
			//this will be the case when a div tag representing a TileTag is encountered
			} else if (!_tmpTag.getClass().equals(TileTag.class)) {
				_tmpText = text;
			}
		} catch (FrameworkException e) {
			LOGGER.logException(e);
		}
	}

	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		try {
			if(!_tmpText.equals("")) {
				_tmpTag.setPreEndTagText(_tmpText);
				_tmpText = "";
			}
			
			if(null != _tmpTag.getParent()) {
				_tmpTag = _tmpTag.getParent();
			}
		} catch (FrameworkException e) {
			LOGGER.logException(e);
		}
	}
	
	private List<Attribute> getAttributesFromPage(Attributes xmlAtts) {
		boolean classAttribute = false;
		List<Attribute> atts = new ArrayList<Attribute>();
		//add attributes
		for (int i = 0; i < xmlAtts.getLength(); i++) {
			String name = xmlAtts.getLocalName(i);
			if(name.equals("class") || name.equals(FrameworkConstants.PISTON_TILE_ID) || name.equals(FrameworkConstants.PISTON_START_ACTION)) {
				String value = xmlAtts.getValue(i);
				Attribute tempAtt = new Attribute(name, value);
				atts.add(tempAtt);
				if(name.equals("class")) {
					classAttribute = true;
				}
			}
		}
		//check for style attribute
		for (int i = 0; i < xmlAtts.getLength(); i++) {
			String name = xmlAtts.getLocalName(i);
			if(name.equals("style") && !classAttribute) {
				String value = xmlAtts.getValue(i);
				Attribute tempAtt = new Attribute(name, value);
				atts.add(tempAtt);
				break;
			}
		}
		
		return atts;
	}
	
	private List<Attribute> getAttributesFromXML(Attributes xmlAtts) {
		List<Attribute> atts = new ArrayList<Attribute>();
		for (int i = 0; i < xmlAtts.getLength(); i++) {
			String name = xmlAtts.getLocalName(i);
			String value = xmlAtts.getValue(i);
			Attribute att = new Attribute(name, value);
			atts.add(att);
		}
		return atts;
	}
	
	private boolean checkTile(List<Attribute> atts) {
		for(Attribute att : atts){
			String name = att.getName();
			if(name.equals(FrameworkConstants.PISTON_TILE_ID) || name.equals(FrameworkConstants.PISTON_START_ACTION)) {
				return true;
			}
		}
		return false;
	}
}
